package edu.gzhu.guli.core.security.jwt;

import com.fasterxml.jackson.databind.ObjectMapper;

import edu.gzhu.guli.core.security.SecurityUser;
import lombok.Data;

import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

  private final AuthenticationManager authenticationManager;
  private final ObjectMapper objectMapper = new ObjectMapper();
  private final ThreadLocal<Boolean> rememberMe = ThreadLocal.withInitial(() -> false);

  public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
    super.setFilterProcessesUrl(SecurityConstants.AUTH_LOGIN_URL);
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException {
    try {
      LoginUser loginUser = objectMapper.readValue(request.getInputStream(), LoginUser.class);
      rememberMe.set(loginUser.isRememberMe());
      // kind只能是ADMIN或MEMBER
      if (!loginUser.getKind().equals("ADMIN") && !loginUser.getKind().equals("MEMBER")) return null;
      String username = loginUser.getKind() + ':' +loginUser.getUsername();
      Authentication authentication = new UsernamePasswordAuthenticationToken(username,
          loginUser.getPassword());
      return authenticationManager.authenticate(authentication);
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request,
      HttpServletResponse response,
      FilterChain chain,
      Authentication authResult) throws IOException, ServletException {
    SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
    List<String> roles = securityUser.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList());
    String token = JwtTokenUtils
        .createToken(securityUser.getUsername(), roles, rememberMe.get());
    response.setHeader(SecurityConstants.TOKEN_HEADER, token);
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.setCharacterEncoding("utf-8");
    objectMapper.writeValue(response.getWriter(), securityUser);
  }

  @Override
  protected void unsuccessfulAuthentication(HttpServletRequest request,
      HttpServletResponse response,
      AuthenticationException failed) throws IOException, ServletException {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户名或密码错误");
  }

  @Data
  private static class LoginUser {
    private String username;
    private String password;
    private String kind;
    private boolean rememberMe;
  }
}
